package com.gitee.wsl.ext.collection


/**
 * Returns the single element matching the given [predicate], or `null` if element was not found.
 *
 * Throws [IllegalArgumentException] when multiple elements are matching predicate.
 */
inline fun <T> Iterable<T>.singleOrEmpty(predicate: (T) -> Boolean): T? {
    var single: T? = null
    var found = false
    for (element in this) {
        if (predicate(element)) {
            if (found) {
                throw IllegalArgumentException("Collection contains more than one matching element.")
            }
            single = element
            found = true
        }
    }
    return single
}

/**
 * Returns single element, or `null` if the collection is empty.
 * Throws [IllegalArgumentException] when multiple elements are matching predicate.
 */
fun <T> Iterable<T>.singleOrEmpty(): T? =
    when (this) {
        is List ->
            when (size) {
                0 -> null
                1 -> this[0]
                else -> throw IllegalArgumentException("Collection contains more than one element.")
            }
        else -> {
            val iterator = iterator()
            if (!iterator.hasNext()) {
                null
            } else {
                val single = iterator.next()
                if (iterator.hasNext()) {
                    throw IllegalArgumentException("Collection contains more than one element.")
                }
                single
            }
        }
    }

// see https://discuss.kotlinlang.org/t/singleornull-whats-your-expectation/12085/14
fun <T> Iterable<T>.singleOrNullIfEmpty() =
    if (none()) null else single()
